package cn.virens.common.apifox.util;

import cn.hutool.core.util.*;
import cn.virens.common.apifox.ApifoxType;
import cn.virens.common.apifox.build.ApiFoxCollectionRequestJson;
import cn.virens.common.entity.BaseModel;
import cn.virens.common.exception.APIException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONField;
import io.mybatis.provider.Entity.Column;
import org.springframework.web.bind.annotation.RequestBody;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Map;

public class ApifoxUtilBody {
    /**
     * 获取请求对象
     */
    public static ApiFoxCollectionRequestJson getRequest(Class<?> clazz, Method method) throws APIException {
        return Arrays.stream(method.getParameters()).filter(parameters -> {
            return parameters.getAnnotation(RequestBody.class) != null;
        }).findAny().map(m -> parse(getType(clazz, m))).orElse(null);
    }

    /**
     * 根据类型Class生成对应的参数描述内容
     */
    private static ApiFoxCollectionRequestJson parse(Class<?> clazz) throws APIException {
        if (ClassUtil.isAssignable(Iterable.class, clazz)) return parseArray(clazz);
        if (ClassUtil.isAssignable(JSON.class, clazz)) return parseMap(clazz);
        if (ClassUtil.isAssignable(Map.class, clazz)) return parseMap(clazz);

        if (clazz != null) {
            return parseObject(clazz);
        } else {
            return null;
        }
    }

    /**
     * 根据Map生成无参数对象
     */
    private static ApiFoxCollectionRequestJson parseMap(Class<?> clazz) throws APIException {
        ApiFoxCollectionRequestJson schema = new ApiFoxCollectionRequestJson();
        schema.setType("object");

        return schema;
    }

    /**
     * 根据对象类型生成参数
     */
    private static ApiFoxCollectionRequestJson parseObject(Class<?> clazz) throws APIException {
        ApiFoxCollectionRequestJson schema = new ApiFoxCollectionRequestJson();
        schema.setType("object");

        Arrays.stream(ReflectUtil.getFields(clazz)).forEach(field -> {
            ApiFoxCollectionRequestJson item = new ApiFoxCollectionRequestJson();
            item.setType(ApifoxType.formatType(field.getType()));
            item.setTitle(getTitle(field));
            item.setFname(getName(field));

            if (StrUtil.isAllNotEmpty(item.getFname(), item.getTitle())) {
                schema.addProperties(item.getFname(), item);
            }
        });

        return schema;
    }

    /**
     * 根据列表生成无参数数组
     */
    private static ApiFoxCollectionRequestJson parseArray(Class<?> clazz) throws APIException {
        ApiFoxCollectionRequestJson items = new ApiFoxCollectionRequestJson();
        items.setType("object");

        ApiFoxCollectionRequestJson schema = new ApiFoxCollectionRequestJson();
        schema.addProperties("items", items);
        schema.setType("array");

        return schema;
    }

    /**
     * 获取字段备注
     */
    private static String getTitle(Field field) throws APIException {
        Column anno = field.getAnnotation(Column.class);
        if (anno == null || anno.remark() == null) {
            return field.getName();
        } else {
            return anno.remark();
        }
    }

    /**
     * 获取字段名称
     */
    private static String getName(Field field) throws APIException {
        JSONField anno = field.getAnnotation(JSONField.class);
        if (anno == null || anno.name() == null) {
            return field.getName();
        } else {
            return anno.name();
        }
    }

    /**
     * 获取参数类型（需尝试获取泛型）
     */
    private static Class<?> getType(Class<?> clazz, Parameter parameter) {
        if (clazz != null && parameter.getType() == BaseModel.class) {
            Class<?> answer = getBaseArgType(clazz);
            if (answer != null) {
                return answer;
            }
        }

        return parameter.getType();
    }

    /**
     * 尝试从所在类获取泛型当做参数对象
     */
    private static Class<?> getBaseArgType(Class<?> objClass) {
        return TypeUtil.getClass(ArrayUtil.firstMatch(t -> {
            return isExtendsBase(TypeUtil.getClass(t));
        }, TypeUtil.getTypeArguments(objClass)));
    }

    /**
     * 判定泛型是否继承与BaseModel
     */
    private static boolean isExtendsBase(Class<?> clazz) {
        return BaseModel.class.isAssignableFrom(clazz);
    }

}
